package com.gitbl.webchat.web.control;

import com.alibaba.fastjson.JSONObject;
import com.gitbl.webchat.message.JoinMessage;
import com.gitbl.webchat.message.RoomMessage;
import com.gitbl.webchat.service.RoomService;
import com.gitbl.webchat.web.webmessage.WebUser;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.net.URI;


//web socket。负责转发到room node。 只做转发和连接后校验用户
@ServerEndpoint("/roomws/{id}/{token}")
@RestController
public class RoomWSController extends BaseController {
    public class RTransfer extends WebSocketClient {
        RoomWSController spr;

        public RTransfer(URI serverUri, RoomWSController spr) {
            //super(serverUri,new Draft_6455());
            super(serverUri);
            this.spr = spr;
            cache = new LinkedList<>();
            this.connect();
        }

        List<String> cache;

        public synchronized void sendString(String s) {
            //远程端如果还未准备好就先缓存用户消息
            if (!this.getReadyState().equals(READYSTATE.OPEN)) {
                if (cache.size() > 10) {    //可认为是攻击,断开连接
                    this.close();
                    return;
                }
                cache.add(s);
            } else if (cache.size() == 0) {
                this.send(s);
            }
        }

        //发送缓存消息
        synchronized void sendCahces() {
            for (String s : cache) {
                this.send(s);
            }
            cache.clear();
        }

        @Override
        public void onOpen(ServerHandshake serverHandshake) {
            //连接成功后发送缓存消息，第一条必然是 JoinMessage
            sendCahces();
        }

        @Override
        public void onMessage(String s) {
            try {
                spr.session.getBasicRemote().sendText(s);
            } catch (IOException e) {
                e.printStackTrace();
                this.close();

            }
        }

        @Override
        public void onClose(int i, String s, boolean b) {
            try {
                spr.session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onError(Exception e) {
            try {
                spr.session.close();
                this.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    // 用来记录当前连接数的变量
    private static volatile int onlineCount = 0;

    // concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象
    private static CopyOnWriteArraySet<RoomWSController> webSocketSet = new CopyOnWriteArraySet<RoomWSController>();

    // 与某个客户端的连接会话，需要通过它来与客户端进行数据收发
    private Session session;

    private static final Logger LOGGER = LoggerFactory.getLogger(RoomWSController.class);
    WebUser user;
    RTransfer realroom;

    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id, @PathParam("token") String token) throws Exception {
        this.session = session;
        RoomMessage room = roomService.getRoom(id);
        if (room == null) {
            session.close();
            return;
        }
        WebUser u = getUserByToken(token);
        if (u == null) {
            session.close();
        }
        this.user = u;
        JoinMessage join = new JoinMessage();
        join.setRoomId(id);
        join.setUsername(u.getUsername());
        realroom = new RTransfer(new URI(room.getAddress()), this);
        //必然会进缓存
        realroom.sendString(JSONObject.toJSONString(join));

    }

    @OnClose
    public void onClose() {
        realroom.close();
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        realroom.sendString(message);

    }

    @OnError
    public void onError(Session session, Throwable error) throws IOException {

        session.close();
        realroom.close();
    }


}
